#include "Timer.h"
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#include "util/Helper.h"
#include "Configuration.h"
#include "event/Events.h"
#include "Messenger.h"
#include "ConfigurationManager.h"
#include "util/Helper.h"



using namespace Boon;

void Boon::Timer::OnInitialize()
{
	ConfigurationManager& cm = ConfigurationManager::GetInstance();
	m_TimeStartup = TimeStamp();
	m_FrameCount = 0;
	m_DeltaTime = m_DeltaTimeSmoothed = m_TimerRendering = m_TimerFixedUpdate = .0f;
	m_DeltaTimeFixed = 1.0f / cm.GetFPSPhysics(); // desired physics fps
	m_DeltaTimeRendering =  1.0f / cm.GetFPSRendering(); //desired rendering fps
	m_TimeScale = 1.0f;
	m_FpsLogic = .0f;
	m_FpsRendering = m_DeltaTimeRendering; // start value of wanted fps
	m_SmootingDT = cm.GetDeltaTimeSmoothingFactor(); //factor for calculating avg-update-fps
	m_SmoothingDTRendering = cm.GetDeltaTimeRenderingSmoothingFactor();  //factor for calculating avg-render-update-fps
	m_PhysicsMaxSubsteps = cm.GetPhysicsMaxSubsteps();

	// register events
	EventListener listener( this, &Timer::OnFrameRendered );
	Messenger::GetInstance().Register( Event_FrameRendered::ID, listener );
	listener.bind( this, &Timer::OnPhysicUpdate );
	Messenger::GetInstance().Register( Event_Physics_Update::ID, listener );
	listener.bind( this, &Timer::OnConfigiurationUpdate );
	Messenger::GetInstance().Register( Event_ConfigurationUpdate::ID, listener );
	listener.bind( this, &Timer::OnTimescaleChange );
	Messenger::GetInstance().Register( Event_Timescale_Change::ID, listener );
}

void Boon::Timer::OnDestroy()
{
	// unregister events
	EventListener listener( this, &Timer::OnFrameRendered );
	Messenger::GetInstance().Remove( Event_FrameRendered::ID, listener );
	listener.bind( this, &Timer::OnPhysicUpdate );
	Messenger::GetInstance().Remove( Event_Physics_Update::ID, listener );
	listener.bind( this, &Timer::OnConfigiurationUpdate );
	Messenger::GetInstance().Remove( Event_ConfigurationUpdate::ID, listener );
	listener.bind( this, &Timer::OnTimescaleChange );
	Messenger::GetInstance().Remove( Event_Timescale_Change::ID, listener );
}

void Boon::Timer::TimeScale( float scale )
{
	// important to change timescale only before/after updating -> consitency
	if( scale >= 0 )
		Messenger::GetInstance().BangQueued( IMessengerEventStrong( new Event_Timescale_Change( scale ) ) );
}

const uint_32 Boon::Timer::Time() const
{
	return TimeStamp() - m_TimeStartup;
}

const uint_32 Boon::Timer::TimeStamp()
{
	return timeGetTime();
}

float CalculateDt( uint_32& lastTime )
{
	uint_32 now = Timer::TimeStamp();
	double dt = double( ( now - lastTime ) * .001f );
	lastTime = now;
	return (float)dt;
}

void Boon::Timer::CalculateDeltaTime()
{
	static uint_32 lastTime = TimeStamp();
	m_DeltaTime = CalculateDt( lastTime ) * m_TimeScale;
	m_DeltaTimeSmoothed = Lerp( m_DeltaTimeSmoothed, m_DeltaTime, m_SmootingDT );
	m_FpsLogic = 1.0f / m_DeltaTimeSmoothed;
}

bool ExceededTimer( uint_32& lastTime, float& timer, float time ) 
{
	timer += CalculateDt( lastTime );
	if( timer >= time )	return true;
	return false;
}

bool Boon::Timer::CalculateDeltaTimeRendering()
{
	static uint_32 lastTime = TimeStamp();
	return ExceededTimer( lastTime, m_TimerRendering, m_DeltaTimeRendering );
}

uint_32 Boon::Timer::CalculateDeltaTimeFixed()
{
	static uint_32 lastTime = TimeStamp();
	if( ExceededTimer( lastTime, m_TimerFixedUpdate, m_DeltaTimeFixed ) )
	{
		//always keep up simulation time to (nearly) real time with fixed-size substeps
		uint_32 substeps = Min( uint_32( m_TimerFixedUpdate / m_DeltaTimeFixed ), m_PhysicsMaxSubsteps );
		m_TimerFixedUpdate -= float( m_DeltaTimeFixed ) * substeps;
		return substeps;
	}
	return 0; // no stepping
}

void Boon::Timer::OnFrameRendered( const IMessengerEventStrong& edata )
{
	static uint_32 framesLast = 0;
	++m_FrameCount;
	// calculate current rendering frame rate
	m_FpsRendering = Lerp(	m_FpsRendering,
							( m_FrameCount - framesLast ) / m_TimerRendering,
							m_SmoothingDTRendering );
	framesLast = m_FrameCount;
	m_TimerRendering = .0f;
}

void Boon::Timer::OnPhysicUpdate( const IMessengerEventStrong& edata )
{

}

void Boon::Timer::OnConfigiurationUpdate( const IMessengerEventStrong& edata )
{
	ConfigurationManager& cm = ConfigurationManager::GetInstance();
	m_DeltaTimeFixed = 1.0f / cm.GetFPSPhysics(); // desired physics fps
	m_DeltaTimeRendering =  1.0f / cm.GetFPSRendering(); //desired rendering fps
	m_SmootingDT = cm.GetDeltaTimeSmoothingFactor(); //factor for calculating avg-update-fps
	m_SmoothingDTRendering = cm.GetDeltaTimeRenderingSmoothingFactor();  //factor for calculating avg-render-update-fps
	m_PhysicsMaxSubsteps = cm.GetPhysicsMaxSubsteps();
}

void Boon::Timer::OnTimescaleChange( const IMessengerEventStrong& edata )
{
	Event_Timescale_Change* event = static_cast<Event_Timescale_Change*>( edata.Get() );
	m_TimeScale = event->m_TimeScale;
}


